package com.ruoyi.web.controller.system;

import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.system.domain.SysFileInfo;
import com.ruoyi.system.service.ISysFileInfoService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;
import java.util.List;

/**
 * 文件管理 控制层
 */
@RestController
@RequestMapping("/system/fileinfo")
public class SysFileInfoController extends BaseController {

    private static final Logger logger = LoggerFactory.getLogger(SysFileInfoController.class);

    @Autowired
    private ISysFileInfoService fileService;

    @Autowired
    private RuoYiConfig ruoYiConfig;

    /**
     * 查询文件列表
     */
    @PreAuthorize("@ss.hasPermi('system:fileinfo:list')")
    @GetMapping("/list")
    public TableDataInfo list(SysFileInfo sysFileInfo) {
        startPage();
        List<SysFileInfo> list = fileService.selectFileList(sysFileInfo);
        return getDataTable(list);
    }

    /**
     * 文件上传
     */
    @PreAuthorize("@ss.hasPermi('system:fileinfo:upload')")
    @Log(title = "文件管理", businessType = BusinessType.INSERT)
    @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public AjaxResult uploadFile(@RequestPart("file") MultipartFile file) throws Exception {
        try {
            // 上传文件路径
            String filePath = RuoYiConfig.getUploadPath();
            // 修复URL生成逻辑，不再添加/profile前缀
            String url = FileUploadUtils.upload(filePath, file);
            // 上传并返回新文件名称
            String fileName = FileUtils.getName(url);

            SysFileInfo sysFileInfo = new SysFileInfo();
            sysFileInfo.setName(fileName);
            sysFileInfo.setOriginalName(file.getOriginalFilename());
            sysFileInfo.setUrl(url);
            sysFileInfo.setSize(file.getSize());
            sysFileInfo.setType(FileUploadUtils.getExtension(file));

            fileService.insertFile(sysFileInfo);

            AjaxResult ajax = AjaxResult.success();
            ajax.put("fileId", sysFileInfo.getFileId());
            ajax.put("fileName", fileName);
            ajax.put("url", url);
            return ajax;
        } catch (Exception e) {
            return AjaxResult.error(e.getMessage());
        }
    }

    /**
     * 文件下载
     */
    @PreAuthorize("@ss.hasPermi('system:fileinfo:download')")
    @Log(title = "文件管理", businessType = BusinessType.EXPORT)
    @GetMapping("/download/{fileId}")
    public void download(@PathVariable Long fileId, HttpServletResponse response) throws Exception {
        SysFileInfo sysFileInfo = fileService.selectFileById(fileId);
        if (sysFileInfo != null) {

            String fileUrl = removeProfilePrefix(sysFileInfo.getUrl());

            String filePath = RuoYiConfig.getProfile() + fileUrl;
            downloadFile(response, filePath, sysFileInfo.getOriginalName());
        }
    }

    /**
     * 移除URL中的/profile前缀（如果存在）
     *
     * @param url 文件URL
     * @return 处理后的URL
     */
    private String removeProfilePrefix(String url) {
        if (url != null && url.startsWith("/profile/")) {
            return url.substring(8); // 移除"/profile"前缀，保留开头的"/"
        }
        return url;
    }

    /**
     * 文件预览
     */
    @PreAuthorize("@ss.hasPermi('system:fileinfo:preview')")
    @GetMapping("/preview/{fileId}")
    public AjaxResult preview(@PathVariable Long fileId) {
        SysFileInfo sysFileInfo = fileService.selectFileById(fileId);
        if (sysFileInfo != null) {
            return AjaxResult.success(sysFileInfo);
        }
        return AjaxResult.error("文件不存在");
    }

    /**
     * 匿名访问的图片展示接口（用于登录页背景等）
     */
    @Anonymous
    @GetMapping("/display/{fileId}")
    public void displayImage(@PathVariable Long fileId, HttpServletResponse response) {
        try {
            SysFileInfo sysFileInfo = fileService.selectFileById(fileId);
            if (sysFileInfo != null) {
                String fileUrl = removeProfilePrefix(sysFileInfo.getUrl());
                String filePath = RuoYiConfig.getProfile() + fileUrl;
                
                File file = new File(filePath);
                if (file.exists()) {
                    // 设置响应头
                    String contentType = getContentType(sysFileInfo.getType());
                    response.setContentType(contentType);
                    response.setHeader("Cache-Control", "max-age=3600"); // 1小时缓存
                    
                    // 输出文件内容
                    try (RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
                         FileChannel fileChannel = randomAccessFile.getChannel();
                         WritableByteChannel writableByteChannel = Channels.newChannel(response.getOutputStream())) {
                        
                        fileChannel.transferTo(0, fileChannel.size(), writableByteChannel);
                    }
                    return;
                }
            }
            response.sendError(404, "图片不存在");
        } catch (Exception e) {
            try {
                response.sendError(500, "服务器错误");
            } catch (IOException ioException) {
                logger.error("响应错误信息失败", ioException);
            }
        }
    }

    /**
     * 根据文件类型获取Content-Type
     */
    private String getContentType(String fileType) {
        if (fileType == null) {
            return "application/octet-stream";
        }
        
        String lowerType = fileType.toLowerCase();
        switch (lowerType) {
            case "jpg":
            case "jpeg":
                return "image/jpeg";
            case "png":
                return "image/png";
            case "gif":
                return "image/gif";
            case "bmp":
                return "image/bmp";
            case "webp":
                return "image/webp";
            case "svg":
                return "image/svg+xml";
            default:
                return "application/octet-stream";
        }
    }

    /**
     * 删除文件
     */
    @PreAuthorize("@ss.hasPermi('system:fileinfo:remove')")
    @Log(title = "文件管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/{fileIds}")
    public AjaxResult remove(@PathVariable Long[] fileIds) {
        return toAjax(fileService.deleteFileByIds(fileIds));
    }

    /**
     * 下载文件
     *
     * @param response 响应对象
     * @param filePath 文件路径
     * @param fileName 文件名称
     * @throws IOException IO异常
     */
    private void downloadFile(HttpServletResponse response, String filePath, String fileName) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            response.sendError(404, "文件不存在");
            return;
        }

        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment; filename=" + java.net.URLEncoder.encode(fileName, "UTF-8"));
        response.setContentLength((int) file.length());

        try (RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
             FileChannel fileChannel = randomAccessFile.getChannel();
             WritableByteChannel writableByteChannel = Channels.newChannel(response.getOutputStream())) {

            fileChannel.transferTo(0, fileChannel.size(), writableByteChannel);
        }
    }
}
